﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace Tetris
{
    public partial class Form1 : Form
    {
        string fPathSave;

        Size sz = new Size(10, 20);
        int step = 20;
        bool[,] cl;

        Figure ActFgr = null;
        Figure NextFgr = null;

        int Speed = 500;
        int ClickSpeedLeftRight = 60;
        int ClickSpeedLeftRightFirst = 150;
        bool bFirstClickLeftRight = true;
        int ClickSpeedDown = 30;

        bool bGameOver = false;
        bool bPause = false;

        Keys ActKey = Keys.None;

        int CntLines;
        int CntPoints;

        public Form1()
        {
            InitializeComponent();
            fPathSave = Application.StartupPath + "\\Save.save";
            im.Image = new Bitmap(im.Width, im.Height);
            imNext.Image = new Bitmap(imNext.Width, imNext.Height);
            SetNewGame(File.Exists(fPathSave));
            tmrMain.Start();
        }

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.Space)
            {
                if (!bGameOver) bPause = !bPause;
                if (bPause) SaveData();
                else
                {
                    ClearData();
                    NextMove = DateTime.Now.AddMilliseconds(Speed);
                }
            }

            if (e.KeyData == Keys.Up)
            {
                ActFgr.Rotate(cl, sz);
                DrawAll();
            }

            if (ActKey == Keys.None && (e.KeyData == Keys.Left || e.KeyData == Keys.Right || e.KeyData == Keys.Down))
            {
                ActKey = e.KeyData;
                NextCick = DateTime.Now;
            }

        }

        private void Form1_KeyUp(object sender, KeyEventArgs e)
        {
            ActKey = Keys.None;
            bFirstClickLeftRight = true;
        }

        void LoadData()
        {
            if (!File.Exists(fPathSave)) return;
            using (FileStream st = new FileStream(fPathSave, FileMode.Open))
            {
                using (BinaryReader br = new BinaryReader(st))
                {
                    br.ReadInt32(); // версия
                    NextFgr = new Figure(br); // следующая фигура
                    ActFgr = new Figure(br); // текущая фигура
                    // скорость
                    Speed = br.ReadInt32();
                    // размер
                    sz = new Size(br.ReadInt32(), br.ReadInt32());
                    // матрица
                    cl = new bool[sz.Width, sz.Height];
                    for (int i = 0; i < sz.Width; i++)
                        for (int j = 0; j < sz.Height; j++)
                            cl[i, j] = br.ReadBoolean();

                    // линии и очки
                    CntLines = br.ReadInt32();
                    CntPoints = br.ReadInt32();
                }
            }
        }

        void SaveData()
        {
            using (MemoryStream ms = new MemoryStream())
            {
                using (BinaryWriter bw = new BinaryWriter(ms))
                {
                    bw.Write(0); // версия
                    NextFgr.Save(bw); // следующая фигура
                    ActFgr.Save(bw); // текущая фигура
                    // скорость
                    bw.Write(Speed);
                    // размер
                    bw.Write(sz.Width);
                    bw.Write(sz.Height);
                    // матрица
                    for (int i = 0; i < sz.Width; i++)
                        for (int j = 0; j < sz.Height; j++)
                            bw.Write(cl[i, j]);

                    // линии и очки
                    bw.Write(CntLines);
                    bw.Write(CntPoints);
                }
                ms.Flush();
                File.WriteAllBytes(fPathSave, ms.ToArray());
            }
        }

        void ClearData()
        {
            if (File.Exists(fPathSave)) File.Delete(fPathSave);
        }

        DateTime NextMove = DateTime.Now;
        DateTime NextCick = DateTime.Now;

        int Fps = 0;
        DateTime dtFps = DateTime.Now;
        private void tmrMain_Tick(object sender, EventArgs e)
        {
            if (DateTime.Now.Second != dtFps.Second)
            {
                dtFps = DateTime.Now;
                //Text = "FPS: " + Fps.ToString();
                Fps = 0;
            }

            if (bGameOver || bPause)
            {
                DrawAll();
                return;
            }

            // действия по нажатию клавиш
            if (ActKey != Keys.None && DateTime.Now >= NextCick)
            {
                if (ActKey == Keys.Left || ActKey == Keys.Right)
                {
                    if (ActKey == Keys.Left)
                    {
                        if (!ActFigureIsStopedLeft())
                            ActFgr.Pos.X--;
                        DrawAll();
                    }
                    if (ActKey == Keys.Right)
                    {
                        if (!ActFigureIsStopedRight())
                            ActFgr.Pos.X++;
                        DrawAll();
                    }
                    NextCick = NextCick.AddMilliseconds(bFirstClickLeftRight ? ClickSpeedLeftRightFirst : ClickSpeedLeftRight);
                    bFirstClickLeftRight = false;
                }
                if (ActKey == Keys.Down)
                {
                    NextMove = DateTime.Now;
                    NextCick = NextCick.AddMilliseconds(ClickSpeedDown);
                }

            }

            if (DateTime.Now >= NextMove)
            {
                ClearData();
                if (ActFigureIsStoped())
                {
                    FillBlocksFromFigure();
                    DeleteFullLines();
                    GenerateNewFigure();
                    bGameOver = IsGameOver();
                }
                else
                {
                    ActFgr.Pos.Y++;
                }
                DrawAll();
                NextMove = NextMove.AddMilliseconds(Speed);
            }
            Fps++;
        }

        void DrawAll()
        {
            Graphics gr = Graphics.FromImage(im.Image);
            gr.Clear(Color.White);

            // фигура
            if (!bGameOver)
            {
                for (int i = 0; i < ActFgr.Sz; i++)
                    for (int j = 0; j < ActFgr.Sz; j++)
                    {
                        int x = ActFgr.Pos.X + i;
                        int y = ActFgr.Pos.Y + j;
                        if (x >= 0 && y >= 0 && x < sz.Width && y < sz.Height)
                        {
                            if (ActFgr.Cl[i, j])
                                gr.FillRectangle(Brushes.Blue, x * step, y * step, step, step);
                        }
                    }
                DrawNextFigure();
            }

            // поле и блоки
            for (int i = 0; i < sz.Width; i++)
                for (int j = 0; j < sz.Height; j++)
                {
                    if (cl[i, j]) gr.FillRectangle(Brushes.Navy, i * step, j * step, step, step);
                    gr.DrawRectangle(Pens.LightGray, i * step, j * step, step, step);
                }

            if (bGameOver)
            {
                int x = sz.Width * step / 2 - 100;
                int y = sz.Height * step / 2 - 30;
                gr.DrawString("Game Over", new Font("Arial", 28, FontStyle.Bold), Brushes.White, x - 1, y - 1);
                gr.DrawString("Game Over", new Font("Arial", 28, FontStyle.Bold), Brushes.White, x + 1, y + 1);
                gr.DrawString("Game Over", new Font("Arial", 28, FontStyle.Bold), Brushes.White, x - 1, y + 1);
                gr.DrawString("Game Over", new Font("Arial", 28, FontStyle.Bold), Brushes.White, x - 1, y + 1);
                gr.DrawString("Game Over", new Font("Arial", 28, FontStyle.Bold), Brushes.DarkRed, x, y);
            }

            if (bPause)
            {
                int x = sz.Width * step / 2 - 70;
                int y = sz.Height * step / 2 - 30;
                gr.DrawString("Пауза", new Font("Arial", 28, FontStyle.Bold), Brushes.White, x - 1, y - 1);
                gr.DrawString("Пауза", new Font("Arial", 28, FontStyle.Bold), Brushes.White, x + 1, y + 1);
                gr.DrawString("Пауза", new Font("Arial", 28, FontStyle.Bold), Brushes.White, x - 1, y + 1);
                gr.DrawString("Пауза", new Font("Arial", 28, FontStyle.Bold), Brushes.White, x - 1, y + 1);
                gr.DrawString("Пауза", new Font("Arial", 28, FontStyle.Bold), Brushes.DarkRed, x, y);
            }

            // окантовка фигуры (для теста)
            //gr.DrawRectangle(Pens.Red, ActFgr.Pos.X * step, ActFgr.Pos.Y * step, ActFgr.Sz * step, ActFgr.Sz * step);

            gr.Dispose();
            im.Refresh();
        }

        void DrawNextFigure()
        {
            Graphics gr = Graphics.FromImage(imNext.Image);
            gr.Clear(Color.White);

            for (int i = 0; i < NextFgr.Sz; i++)
                for (int j = 0; j < NextFgr.Sz; j++)
                {
                    int x = i * step + step;
                    int y = j * step + step + step / 2;
                    if (NextFgr.Tp == FiguteType.Line) { y -= step; }
                    if (NextFgr.Tp == FiguteType.Cub) { x += step / 2; }

                    if (NextFgr.Cl[i, j])
                    {
                        gr.FillRectangle(Brushes.Blue, x, y, step, step);
                        gr.DrawRectangle(Pens.LightGray, x, y, step, step);
                    }

                }

            gr.Dispose();
            imNext.Refresh();
        }

        Random rnd = new Random();

        void FillBlocksFromFigure()
        {
            for (int i = 0; i < ActFgr.Sz; i++)
                for (int j = 0; j < ActFgr.Sz; j++)
                {
                    int x = ActFgr.Pos.X + i;
                    int y = ActFgr.Pos.Y + j;
                    if (ActFgr.Cl[i, j])
                        cl[x, y] = true;
                }
        }

        void GenerateNewFigure()
        {
            if (ActKey == Keys.Down) ActKey = Keys.None;
            FiguteType tp = (FiguteType)rnd.Next(7);
            ActFgr = NextFgr;
            NextFgr = new Figure(tp, new Point(tp == FiguteType.Cub ? sz.Width / 2 - 1 : sz.Width / 2 - 2, 0));
        }

        bool ActFigureIsStoped()
        {
            for (int i = 0; i < ActFgr.Sz; i++)
                for (int j = 0; j < ActFgr.Sz; j++)
                {
                    int x = ActFgr.Pos.X + i;
                    int y = ActFgr.Pos.Y + j + 1;
                    if (ActFgr.Cl[i, j] && (y == sz.Height || cl[x, y])) return true;
                }
            return false;
        }

        bool ActFigureIsStopedLeft()
        {
            for (int i = 0; i < ActFgr.Sz; i++)
                for (int j = 0; j < ActFgr.Sz; j++)
                {
                    int x = ActFgr.Pos.X + i - 1;
                    int y = ActFgr.Pos.Y + j;
                    if (ActFgr.Cl[i, j] && (x < 0 || cl[x, y])) return true;
                }
            return false;
        }

        bool ActFigureIsStopedRight()
        {
            for (int i = 0; i < ActFgr.Sz; i++)
                for (int j = 0; j < ActFgr.Sz; j++)
                {
                    int x = ActFgr.Pos.X + i + 1;
                    int y = ActFgr.Pos.Y + j;
                    if (ActFgr.Cl[i, j] && (x == sz.Width || cl[x, y])) return true;
                }
            return false;
        }

        void DeleteFullLines()
        {
            int cntDelete = 0;
            while (true)
            {
                bool bDelete = false;
                for (int j = 0; j < sz.Height; j++)
                {
                    if (!cl[0, j]) continue;
                    bool bDel = true;
                    for (int i = 0; i < sz.Width; i++) if (!cl[i, j]) { bDel = false; break; }
                    if (bDel)
                    {
                        for (int i = 0; i < sz.Width; i++) cl[i, j] = false;
                        for (int j1 = j; j1 > 0; j1--)
                            for (int i = 0; i < sz.Width; i++)
                                cl[i, j1] = cl[i, j1 - 1];
                        for (int i = 0; i < sz.Width; i++) cl[i, 0] = false;
                        cntDelete++;
                        bDelete = true;
                    }
                }

                if (!bDelete) break;
            }

            if (cntDelete > 0)
            {
                CntLines += cntDelete;
                CntPoints += cntDelete * 100 + ((cntDelete - 1) * 100);
                lblLines.Text = CntLines.ToString();
                lblPoints.Text = CntPoints.ToString();
            }
        }

        bool IsGameOver()
        {
            for (int i = 0; i < sz.Width; i++) if (cl[i, 0]) return true;
            return false;
        }

        void SetNewGame(bool LoadGame)
        {
            btnNewGame.Enabled = false;
            ActKey = Keys.None;
            bFirstClickLeftRight = true;
            CntLines = 0;
            CntPoints = 0;
            NextMove = DateTime.Now.AddMilliseconds(Speed);

            cl = new bool[sz.Width, sz.Height];
            GenerateNewFigure();
            GenerateNewFigure();

            if (LoadGame)
            {
                bPause = true;
                LoadData();
            }

            lblLines.Text = CntLines.ToString();
            lblPoints.Text = CntPoints.ToString();
            DrawAll();
        }

        private void btnNewGame_Click(object sender, EventArgs e)
        {
            SetNewGame(false);
        }


    }
}
